package com.foundryx.shared.core.function;

import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;

/**
 * Try 是一个用于封装可能抛出异常操作的容器类。
 * 它将代码执行结果分为两种状态：
 * - Success：表示操作成功，包含一个值
 * - Failure：表示操作失败，包含一个异常
 *
 * 适用于需要统一处理异常、避免嵌套 try-catch 的场景。
 */
public abstract class Try<T> {

    /**
     * 创建一个成功的 Try 实例。
     *
     * @param value 成功的结果值
     * @return 代表成功状态的 Try
     */
    public static <T> Try<T> success(T value) {
        return new Success<>(value);
    }

    /**
     * 创建一个失败的 Try 实例。
     *
     * @param throwable 抛出的异常
     * @return 代表失败状态的 Try
     */
    public static <T> Try<T> failure(Throwable throwable) {
        return new Failure<>(throwable);
    }

    /**
     * 执行给定的 Supplier 函数，并自动捕获其可能抛出的异常。
     *
     * @param supplier 可能抛出异常的操作
     * @return 封装后的 Try 实例
     */
    public static <T> Try<T> of(Supplier<T> supplier) {
        Objects.requireNonNull(supplier, "Supplier must not be null");
        try {
            return success(supplier.get());
        } catch (Throwable t) {
            return failure(t);
        }
    }

    // —————————————————————— 核心抽象方法 ——————————————————————

    /**
     * 判断当前 Try 是否为成功状态。
     *
     * @return 如果是成功状态返回 true，否则 false
     */
    public abstract boolean isSuccess();

    /**
     * 获取成功时的结果值。
     *
     * @return 成功时的结果
     * @throws IllegalStateException 如果当前状态为失败
     */
    public abstract T get();

    /**
     * 获取失败时的异常信息。
     *
     * @return 失败时的异常对象
     * @throws IllegalStateException 如果当前状态为成功
     */
    public abstract Throwable getThrowable();

    /**
     * 对成功值进行映射转换。
     *
     * @param mapper 转换函数
     * @return 新的 Try 实例
     */
    public abstract <R> Try<R> map(Function<T, R> mapper);

    /**
     * 对成功值进行扁平化映射（flatMap）。
     *
     * @param mapper 返回 Try 的转换函数
     * @return 新的 Try 实例
     */
    public abstract <R> Try<R> flatMap(Function<T, Try<R>> mapper);

    /**
     * 在失败时提供恢复值。
     *
     * @param recovery 恢复函数
     * @return 成功状态的新 Try
     */
    public abstract Try<T> recover(Function<Throwable, T> recovery);

    /**
     * 在失败时提供另一个 Try 来替代。
     *
     * @param recovery 返回 Try 的恢复函数
     * @return 新的 Try 实例
     */
    public abstract Try<T> recoverWith(Function<Throwable, Try<T>> recovery);

    /**
     * 如果是成功状态，则执行消费函数。
     *
     * @param consumer 成功时的消费函数
     */
    public abstract void forEach(Consumer<T> consumer);

    /**
     * 无论成功或失败都执行指定的副作用操作。
     *
     * @param runnable 副作用操作
     */
    public abstract void onFinally(Runnable runnable);

    /**
     * 失败回调
     * @param consumer
     */
    public abstract void onFailure(Consumer<Throwable> consumer);

    /**
     * 成功回调
     * @param consumer
     */
    public abstract void onSuccess(Consumer<T> consumer);

    /**
     * 如果当前状态为失败，则将异常包装成 RuntimeException 抛出。
     * @return 成功状态下的值（仅当 isSuccess() 为 true 时返回）
     * @throws RuntimeException 包装后的原始异常
     */
    public abstract T rethrow();

    /**
     * 如果当前状态为失败，抛出自定义异常。
     *
     * @param exceptionSupplier 异常生成器
     * @return 成功状态下的值
     * @throws X 自定义的异常类型
     */
    public abstract <X extends Throwable> T ifFailureThrow(Supplier<X> exceptionSupplier) throws X;

    /**
     * 如果当前状态为失败，基于原始异常抛出自定义异常。
     *
     * @param exceptionFunction 基于 Throwable 生成自定义异常
     * @return 成功状态下的值
     * @throws X 自定义的异常类型
     */
    public abstract <X extends Throwable> T ifFailureThrow(Function<Throwable, X> exceptionFunction) throws X;

    /**
     * 将 Try 转换为 Optional。
     * 成功时返回 Optional.of(value)，失败时返回 Optional.empty()
     *
     * @return 转换后的 Optional
     */
    public abstract Optional<T> toOptional();

    /**
     * 如果成功则返回值，否则返回默认值。
     *
     * @param defaultValue 默认值
     * @return 成功值或默认值
     */
    public abstract T getOrElse(T defaultValue);

    /**
     * 如果成功则返回值，否则通过 Supplier 提供值。
     *
     * @param supplier 默认值生成器
     * @return 成功值或由 supplier 生成的值
     */
    public abstract T orElse(Supplier<T> supplier);

    /**
     * 对成功和失败分别应用不同的函数，返回统一结果。
     *
     * @param onSuccess 成功时的处理函数
     * @param onFailure 失败时的处理函数
     * @return 统一的结果值
     */
    public abstract <R> R transform(Function<T, R> onSuccess, Function<Throwable, R> onFailure);

    /**
     * 过滤成功值是否满足某个条件。
     *
     * @param predicate 条件判断函数
     * @return 如果满足条件返回原 Try，否则返回失败状态
     */
    public abstract Try<T> filter(Predicate<T> predicate);

    public abstract T orElseThrow(Supplier<Throwable> exceptionSupplier);

    public abstract T orElseThrow(Predicate<T> predicate, Function<T, Throwable> exceptionSupplier);

    /**
     *
     * @return
     */
    public abstract String toString();

    /**
     * 执行给定的操作，并在失败时自动重试指定次数。
     *
     * @param supplier 可能抛出异常的操作
     * @param retries 最大重试次数（不包括首次执行）
     * @param delayMillis 每次重试之间的延迟毫秒数（可设为 0）
     * @return 封装后的 Try 实例
     */
    public static <T> Try<T> retry(Supplier<T> supplier, int retries, long delayMillis) {
        Objects.requireNonNull(supplier, "Supplier must not be null");
        int attempt = 0;
        while (attempt <= retries) {
            Try<T> result = Try.of(supplier);
            if (result.isSuccess()) {
                return result;
            }
            if (attempt < retries) {
                try {
                    Thread.sleep(delayMillis);
                } catch (InterruptedException ignored) {
                    Thread.currentThread().interrupt();
                }
            }
            attempt++;
        }
        return Try.failure(new RuntimeException("Operation failed after " + retries + " retries"));
    }


    // —————————————————————— 内部类：Success ——————————————————————

    private static final class Success<T> extends Try<T> {
        private final T value;

        Success(T value) {
            this.value = value;
        }

        @Override
        public boolean isSuccess() {
            return true;
        }

        @Override
        public T get() {
            return value;
        }

        @Override
        public Throwable getThrowable() {
            throw new IllegalStateException("getThrowable called on Success");
        }

        @Override
        public <R> Try<R> map(Function<T, R> mapper) {
            try {
                return success(mapper.apply(value));
            } catch (Exception e) {
                return failure(e);
            }
        }

        @Override
        public <R> Try<R> flatMap(Function<T, Try<R>> mapper) {
            try {
                return mapper.apply(value);
            } catch (Exception e) {
                return failure(e);
            }
        }

        @Override
        public Try<T> recover(Function<Throwable, T> recovery) {
            return this;
        }

        @Override
        public Try<T> recoverWith(Function<Throwable, Try<T>> recovery) {
            return this;
        }

        @Override
        public void forEach(Consumer<T> consumer) {
            consumer.accept(value);
        }

        @Override
        public void onFinally(Runnable runnable) {
            runnable.run();
        }

        @Override
        public void onFailure(Consumer<Throwable> consumer) {

        }

        @Override
        public void onSuccess(Consumer<T> consumer) {
            consumer.accept(value);
        }

        @Override
        public T rethrow() {
            return value;
        }

        @Override
        public <X extends Throwable> T ifFailureThrow(Supplier<X> exceptionSupplier) throws X {
            return value;
        }

        @Override
        public <X extends Throwable> T ifFailureThrow(Function<Throwable, X> exceptionFunction) throws X {
            return value;
        }

        @Override
        public Optional<T> toOptional() {
            return Optional.of(value);
        }

        @Override
        public T getOrElse(T defaultValue) {
            return value;
        }

        @Override
        public T orElse(Supplier<T> supplier) {
            return value;
        }

        @Override
        public <R> R transform(Function<T, R> onSuccess, Function<Throwable, R> onFailure) {
            return onSuccess.apply(value);
        }

        @Override
        public Try<T> filter(Predicate<T> predicate) {
            if (!predicate.test(value)) {
                return failure(new NoSuchElementException("Predicate does not hold for " + value));
            }
            return this;
        }

        @Override
        public T orElseThrow(Supplier<Throwable> exceptionSupplier) {
            if (value == null) {
                throw new RuntimeException(exceptionSupplier.get());
            }
            return value;
        }

        @Override
        public T orElseThrow(Predicate<T> predicate, Function<T, Throwable> exceptionSupplier) {
            if (predicate.test(value)) {
                return value;
            }
            throw new RuntimeException(exceptionSupplier.apply(value));
        }

        @Override
        public String toString() {
            return "Success(" + get() + ")";
        }
    }

    // —————————————————————— 内部类：Failure ——————————————————————

    private static final class Failure<T> extends Try<T> {
        private final Throwable throwable;

        Failure(Throwable throwable) {
            this.throwable = throwable;
        }

        @Override
        public boolean isSuccess() {
            return false;
        }

        @Override
        public T get() {
            throw new IllegalStateException("Cannot get from a failed Try", throwable);
        }

        @Override
        public Throwable getThrowable() {
            return throwable;
        }

        @Override
        public <R> Try<R> map(Function<T, R> mapper) {
            return new Failure<>(throwable);
        }

        @Override
        public <R> Try<R> flatMap(Function<T, Try<R>> mapper) {
            return new Failure<>(throwable);
        }

        @Override
        public Try<T> recover(Function<Throwable, T> recovery) {
            try {
                return success(recovery.apply(throwable));
            } catch (Exception e) {
                return failure(e);
            }
        }

        @Override
        public Try<T> recoverWith(Function<Throwable, Try<T>> recovery) {
            try {
                return recovery.apply(throwable);
            } catch (Exception e) {
                return failure(e);
            }
        }

        @Override
        public void forEach(Consumer<T> consumer) {
            // do nothing
        }

        @Override
        public void onFinally(Runnable runnable) {
            runnable.run();
        }

        @Override
        public void onFailure(Consumer<Throwable> consumer) {
            consumer.accept(throwable);
        }

        @Override
        public void onSuccess(Consumer<T> consumer) {

        }

        @Override
        public T rethrow() {
            throw new RuntimeException(throwable);
        }

        @Override
        public <X extends Throwable> T ifFailureThrow(Supplier<X> exceptionSupplier) throws X {
            throw exceptionSupplier.get();
        }

        @Override
        public <X extends Throwable> T ifFailureThrow(Function<Throwable, X> exceptionFunction) throws X {
            throw exceptionFunction.apply(throwable);
        }

        @Override
        public Optional<T> toOptional() {
            return Optional.empty();
        }

        @Override
        public T getOrElse(T defaultValue) {
            return defaultValue;
        }

        @Override
        public T orElse(Supplier<T> supplier) {
            return supplier.get();
        }

        @Override
        public <R> R transform(Function<T, R> onSuccess, Function<Throwable, R> onFailure) {
            return onFailure.apply(throwable);
        }

        @Override
        public Try<T> filter(Predicate<T> predicate) {
            return this;
        }

        @Override
        public T orElseThrow(Supplier<Throwable> exceptionSupplier) {
            return null;
        }

        @Override
        public T orElseThrow(Predicate<T> predicate, Function<T, Throwable> exceptionSupplier) {
            return null;
        }

        @Override
        public String toString() {
            return "Failure(" + getThrowable().getClass().getSimpleName() + ": " + getThrowable().getMessage() + ")";
        }
    }
}
